/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007-2008 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.projects.toolbox_basics_project.web.gwt.form.bean.impl;

import java.util.ArrayList;
import java.util.List;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import com.genia.toolbox.basics.bean.Transformer;
import com.genia.toolbox.basics.exception.BundledException;
import com.genia.toolbox.basics.exception.technical.TechnicalException;
import com.genia.toolbox.basics.manager.CollectionManager;
import com.genia.toolbox.projects.toolbox_basics_project.bean.PersistableApplicationParameter;
import com.genia.toolbox.projects.toolbox_basics_project.bean.impl.AbstractDefaultParameterHandler;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.Parameter;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.PrimitiveParameter;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.impl.PrimitiveParameterImpl;
import com.genia.toolbox.projects.toolbox_basics_project.web.gwt.form.bean.FormParameterHandler;
import com.genia.toolbox.web.gwt.basics.client.oracle.SimpleSuggestion;
import com.genia.toolbox.web.gwt.form.client.form.MutableItem;
import com.genia.toolbox.web.gwt.form.client.form.impl.TextBoxItemImpl;
import com.genia.toolbox.web.gwt.form.client.validator.Validator;
import com.genia.toolbox.web.gwt.form.client.validator.impl.AggregateValidator;
import com.genia.toolbox.web.gwt.form.client.validator.impl.RegularExpressionValidator;
import com.genia.toolbox.web.gwt.form.client.value.FormSimpleValue;
import com.genia.toolbox.web.gwt.form.client.value.impl.FormSimpleValueImpl;

/**
 * Default implementation of {@link FormParameterHandler}.
 */
public class DefaultFormParameterHandler
    extends AbstractDefaultParameterHandler
    implements FormParameterHandler<Object>
{

  /**
   * the {@link CollectionManager} to use.
   */
  private CollectionManager collectionManager;



  /**
   * returns the current value of the parameter given in argument.
   * 
   * @param applicationParameter
   *          the parameter to extract the current value from
   * @return the current value of the parameter given in argument
   * @see com.genia.toolbox.projects.toolbox_basics_project.web.gwt.form.bean.FormParameterHandler#getCurrentValue(com.genia.toolbox.projects.toolbox_basics_project.bean.PersistableApplicationParameter)
   */
  public FormSimpleValue getCurrentValue(PersistableApplicationParameter<Object> applicationParameter)
  {
    FormSimpleValueImpl formSimpleValue = new FormSimpleValueImpl();
    formSimpleValue.setName(applicationParameter.getReferenceName());
    formSimpleValue.setValue(applicationParameter.getValue() == null ? null : applicationParameter.getValue().toString());
    return formSimpleValue;
  }



  /**
   * returns the {@link MutableItem} that allow editing of the current
   * parameter.
   * 
   * @param label
   *          the label of this parameter.
   * @param applicationParameter
   *          the parameter to generate the from
   * @return the {@link MutableItem} that allow editing of the current parameter
   * @throws BundledException
   *           if an error occured
   * @see com.genia.toolbox.projects.toolbox_basics_project.web.gwt.form.bean.FormParameterHandler#getForm(com.genia.toolbox.projects.toolbox_basics_project.bean.PersistableApplicationParameter,
   *      String)
   */
  public MutableItem getItem(String label, PersistableApplicationParameter<Object> applicationParameter)
      throws BundledException
  {
    TextBoxItemImpl textBox = new TextBoxItemImpl();
    textBox.setValidator(getValidator(applicationParameter));
    return textBox;
  }



  /**
   * set the current value of the parameter given in argument.
   * 
   * @param applicationParameter
   *          the parameter to set the current value from
   * @param formSimpleValue
   *          the new value of the parameter
   * @throws TechnicalException
   *           if an error occured
   * @see com.genia.toolbox.projects.toolbox_basics_project.web.gwt.form.bean.FormParameterHandler#setCurrentValue(com.genia.toolbox.projects.toolbox_basics_project.bean.PersistableApplicationParameter,
   *      com.genia.toolbox.web.gwt.form.client.value.FormSimpleValue)
   */
  @Transactional(rollbackFor = TechnicalException.class)
  public void setCurrentValue(PersistableApplicationParameter<Object> applicationParameter, FormSimpleValue formSimpleValue)
      throws TechnicalException
  {
    final String stringValue = formSimpleValue.getValue() == null ? null : formSimpleValue.getValue();
    for (String regularExpression : applicationParameter.getValidationRegularExpressions()) {
      if (!stringValue.matches(regularExpression)) {
        throw new TechnicalException(RegularExpressionValidator.DEFAULT_PARSE_ERROR_KEY);
      }
    }
    Object value = getValue(applicationParameter, stringValue);
    if (ObjectUtils.nullSafeEquals(value, applicationParameter.getValue())) {
      return;
    }
    applicationParameter.setValue(value);
    Parameter parameter = getParameterManager().getParameter(applicationParameter.getReferenceName());
    if (parameter != null) {
      if (!(parameter instanceof PrimitiveParameter)) {
        getParameterManager().delete(parameter);
        parameter = null;
      }
    }
    PrimitiveParameter primitiveParameter = (PrimitiveParameter) parameter;
    if (primitiveParameter == null) {
      primitiveParameter = new PrimitiveParameterImpl();
      primitiveParameter.setReferenceName(applicationParameter.getReferenceName());
      getParameterManager().save(primitiveParameter);
    }
    primitiveParameter.setValue(stringValue);
    getParameterManager().update(primitiveParameter);
  }



  /**
   * add a custom validator to the list of validator that this object must
   * verify.
   * 
   * @param applicationParameter
   *          the parameter to generate the {@link Validator} for
   * @return a custom validator to add to the list of validator that this object
   *         must verify or <code>null</code> if no validator must be added
   * @throws BundledException
   *           if an error occured
   */
  public Validator addValidator(PersistableApplicationParameter<Object> applicationParameter)
      throws BundledException
  {
    return null;
  }



  /**
   * returns the {@link Validator} that check that values of the current
   * parameter.
   * 
   * @param applicationParameter
   *          the parameter to generate the {@link Validator} for
   * @return the {@link Validator} that check that values of the current
   *         parameter
   * @throws BundledException
   *           if an error occured
   */
  private Validator getValidator(PersistableApplicationParameter<Object> applicationParameter)
      throws BundledException
  {
    Validator addedValidator = addValidator(applicationParameter);
    List<Validator> validators = new ArrayList<Validator>();
    if (addedValidator != null) {
      validators.add(addedValidator);
    }
    validators.addAll(getCollectionManager().transformList(applicationParameter.getValidationRegularExpressions(), new Transformer<String, Validator>()
    {
      public Validator transform(String object)
          throws BundledException
      {
        return new RegularExpressionValidator(object);
      }
    }));
    switch (validators.size()) {
      case 0:
        return null;
      case 1:
        return validators.get(0);
      default:
        AggregateValidator aggregateValidator = new AggregateValidator();
        aggregateValidator.setValidators(validators);
        return aggregateValidator;
    }
  }



  /**
   * getter for the collectionManager property.
   * 
   * @return the collectionManager
   */
  public CollectionManager getCollectionManager()
  {
    return collectionManager;
  }



  /**
   * setter for the collectionManager property.
   * 
   * @param collectionManager
   *          the collectionManager to set
   */
  public void setCollectionManager(CollectionManager collectionManager)
  {
    this.collectionManager = collectionManager;
  }



  /**
   * get the display string for a given value.
   * 
   * @param applicationParameter
   *          the parameter to get the suggestions for
   * @param value
   *          the current value of the field
   * @return the string to display for the given value
   * @throws BundledException
   *           when an error occurred
   * @see com.genia.toolbox.projects.toolbox_basics_project.web.gwt.form.bean.FormParameterHandler#getDisplayString(com.genia.toolbox.projects.toolbox_basics_project.bean.PersistableApplicationParameter,
   *      java.lang.String)
   */
  public String getDisplayString(PersistableApplicationParameter<Object> applicationParameter, String value)
      throws BundledException
  {
    // Should never be called.
    assert (false);
    return null;
  }



  /**
   * get suggestions for the parameter given in argument.
   * 
   * @param applicationParameter
   *          the parameter to get the suggestions for
   * @param query
   *          the current value of the field
   * @param limit
   *          the maximum number of response
   * @return the list of suggestion for the field
   * @throws BundledException
   *           when an error occurred
   * @see com.genia.toolbox.projects.toolbox_basics_project.web.gwt.form.bean.FormParameterHandler#getSuggestions(com.genia.toolbox.projects.toolbox_basics_project.bean.PersistableApplicationParameter,
   *      java.lang.String, int)
   */
  public List<SimpleSuggestion> getSuggestions(PersistableApplicationParameter<Object> applicationParameter, String query, int limit)
      throws BundledException
  {
    // Should never be called.
    assert (false);
    return null;
  }

}
